// 蜂窝蜜造平台生成代码，如手工更改，请添加到 .beeignore 忽略生成

package com.fowo.api.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.sql.visitor.functions.If;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.config.CacheConfig;
import com.fowo.api.common.excel.CustomExcelExport;
import com.fowo.api.common.excel.CustomExcelHandler;
import com.fowo.api.common.excel.ExcelConfig;
import com.fowo.api.common.excel.TemplateModelBuildEventListener;
import com.fowo.api.common.model.ImportRow;
import com.fowo.api.common.model.JoinModel;
import com.fowo.api.common.model.OptionItem;
import com.fowo.api.common.model.RException;
import com.fowo.api.common.util.ContextHolder;
import com.fowo.api.common.util.DateUtils;
import com.fowo.api.common.util.ResponseUtils;
import com.fowo.api.common.util.SheetUtils;
import com.fowo.api.entity.ListingFather;
import com.fowo.api.entity.PurchasePlans;
import com.fowo.api.entity.ShopInfo;
import com.fowo.api.entity.Warehouse;
import com.fowo.api.flow.entity.WorkFlow;
import com.fowo.api.flow.service.WorkFlowService;
import com.fowo.api.mapper.PurchaseOrderMapper;
import com.fowo.api.mapper.PurchasePlansMapper;
import com.fowo.api.mapper.PurchasePlansProductMapper;
import com.fowo.api.mapper.ShopInfoMapper;
import com.fowo.api.mapper.SupplierMapper;
import com.fowo.api.model.purchase.order.PurchaseOrderItemVo;
import com.fowo.api.model.purchase.order.PurchaseOrderSearchParamPo;
import com.fowo.api.model.purchase.plans.PurchasePlansDetailedInfoParam;
import com.fowo.api.model.purchase.plans.PurchasePlansExcelPo;
import com.fowo.api.model.purchase.plans.PurchasePlansImportPo;
import com.fowo.api.model.purchase.plans.PurchasePlansItemVo;
import com.fowo.api.model.purchase.plans.PurchasePlansPurchasingPlanBoardAction;
import com.fowo.api.model.purchase.plans.PurchasePlansSearchParamPo;
import com.fowo.api.model.purchase.plans.PurchasePlansSyncPurchasePlansAction;
import com.fowo.api.model.purchase.plans.PurchasePlansVo;
import com.fowo.api.model.purchase.plans.constant.PurchasePlansStatusEnums;
import com.fowo.api.model.purchase.plans.product.PurchasePlansProductSearchParamPo;
import com.fowo.api.model.shop.info.ShopInfoItemVo;
import com.fowo.api.model.shop.info.ShopInfoSearchParamPo;
import com.fowo.api.model.shop.info.ShopInfoVo;
import com.fowo.api.model.supplier.SupplierItemVo;
import com.fowo.api.model.supplier.SupplierSearchParamPo;
import com.fowo.api.service.ListingFatherService;
import com.fowo.api.service.PurchasePlansProductService;
import com.fowo.api.service.PurchasePlansService;
import com.fowo.api.sys.component.ExecuteScriptEngine;
import com.fowo.api.sys.entity.*;
import com.fowo.api.sys.mapper.SysAreaMapper;
import com.fowo.api.sys.mapper.SysUserMapper;
import com.fowo.api.sys.model.*;
import com.fowo.api.sys.service.*;
import com.fowo.api.user.model.JwtUserInfo;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 采购计划 服务实现基类
 *
 * @author 蜂窝蜜造平台 配置人：王阳阳
 */
@Slf4j
@DS("ds24")
public class PurchasePlansBaseServiceImpl
        extends ServiceImpl<PurchasePlansMapper, PurchasePlans>
        implements PurchasePlansService {

    /**
     * 引用字段排序字段名转换信息
     */
    public static final Map<String, String> SORT_FIELD_MAP = new HashMap<>() {
        {
            put("countryName", "sa.`name`");
            put("shopShopName", "si.`shop_name`");
            put("supplySupplierName", "s.`supplier_name`");
            put("purchaseWarePurchaseOrderCode", "po.`purchase_order_code`");
            put("createNameName", "su.`name`");
        }
    };
    // 导出时单次查询最大记录数
    protected static final int EXPORT_QUERY_MAX_SIZE = 1000;
    // 支持导出的最大记录数(1048576 为 Excel 支持的最大行数)
    protected static final long EXPORT_MAX_SIZE = 1048575;

    @Resource
    protected PurchasePlansProductService purchasePlansProductService;

    @Resource
    protected SysSerialService sysSerialService;

    @Resource
    private SysDictService sysDictService;

    @Resource
    protected SysAreaMapper sysAreaMapper;

    @Resource
    protected ShopInfoMapper shopInfoMapper;

    @Resource
    protected SupplierMapper supplierMapper;

    @Resource
    protected PurchaseOrderMapper purchaseOrderMapper;

    @Resource
    protected SysUserMapper sysUserMapper;

    @Resource
    protected FileService fileService;

    @Resource
    protected SysImportTemplateService importTemplateService;

    @Resource
    private CustomExcelExport customExcelExport;

    @Resource
    private WorkFlowService workFlowService;

    @Resource
    private ExecuteScriptEngine executeScriptEngine;

    @Autowired(required = false)
    protected PurchasePlansPurchasingPlanBoardAction purchasePlansPurchasingPlanBoardAction;

    @Autowired(required = false)
    protected PurchasePlansSyncPurchasePlansAction purchasePlansSyncPurchasePlansAction;

    @Resource
    protected Validator validator;

    /**
     * 创建采购计划
     *
     * @param model 采购计划
     * @return 新数据的主键
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long create(PurchasePlansVo model) throws Exception {
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        PurchasePlans entity = new PurchasePlans();
        BeanUtils.copyProperties(model, entity);
        entity.setCreateTime(new Date());
        entity.setLastUpdateTime(new Date());
        if (currentUser != null) {
            entity.setCreateUser(currentUser.getUserId());
            entity.setLastUpdateUser(currentUser.getUserId());
        }

        if (model.getStatus() == null) {
            entity.setStatus("0");
        }
        // 填充序列号
        if (!StringUtils.hasText(entity.getPpgSn())) {
            entity.setPpgSn(
                    sysSerialService.getNewSerial(
                            new NewSerialRequest(null, "PurchasePlans.ppgSn")
                    )
            );
        }
        if (this.save(entity)) {
            model.setId(entity.getId());
            updateAllChilds(model);
            baseMapper.updateCalcFields(entity.getId());
            return entity.getId();
        }
        throw new Exception("采购计划保存失败");
    }

    /**
     * 更新所有子表
     *
     * @param model 采购计划
     */
    protected void updateAllChilds(PurchasePlansVo model) throws Exception {
        if (model.getProcurementPlan() != null) {
            purchasePlansProductService.saveAllByParentPurchasePlans(
                    model.getId(),
                    model.getProcurementPlan()
            );
        }
    }

    /**
     * 更新采购计划
     *
     * @param model 采购计划
     * @return 更新是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(PurchasePlansVo model) throws Exception {
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        PurchasePlans entity = this.getById(model.getId());
        if (entity == null) {
            throw new Exception("采购计划不存在");
        }
        BeanUtils.copyProperties(
                model,
                entity,
                "id",
                "createUser",
                "createTime",
                "lastUpdateTime",
                "lastUpdateUser"
        );
        entity.setLastUpdateTime(new Date());
        if (currentUser != null) {
            entity.setLastUpdateUser(currentUser.getUserId());
        }
        if (this.updateById(entity)) {
            updateAllChilds(model);
            baseMapper.updateCalcFields(entity.getId());
            return true;
        }
        return false;
    }

    /**
     * 更新采购计划（带空值）
     *
     * @param model 采购计划
     * @return 更新是否成功
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateForEdit(PurchasePlansVo model) throws Exception {
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        PurchasePlans entity = this.getById(model.getId());
        if (entity == null) {
            throw new Exception("采购计划不存在");
        }
        model.setLastUpdateTime(new Date());
        if (currentUser != null) {
            model.setLastUpdateUser(currentUser.getUserId());
        }
        boolean isOk = new LambdaUpdateChainWrapper<>(baseMapper)
                .set(PurchasePlans::getRemark, model.getRemark())
                .set(PurchasePlans::getFile, model.getFile())
                .set(PurchasePlans::getLastUpdateTime, model.getLastUpdateTime())
                .set(PurchasePlans::getLastUpdateUser, model.getLastUpdateUser())
                .eq(PurchasePlans::getId, model.getId())
                .update();
        if (isOk) {
            updateAllChilds(model);
            baseMapper.updateCalcFields(model.getId());
        }
        return isOk;
    }


    /**
     * 获取店铺下拉框
     * @param fnsku
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Cacheable(
            value = CacheConfig.FAST,
            key = "'PurchasePlans::' + @userInfo.userTypeAndId + '::' + #fnsku",
            unless = "#result == null "
    )
    public JSONArray getShopList(String fnsku) {
        JSONArray result = new JSONArray();
        //仓库属性
        String sysUserPermissionWarehouseDict = "SCM00106";
        //01 获取对应字典
        List<SysDictAllItem> list = sysDictService.getDictItems(sysUserPermissionWarehouseDict);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        //02 循环字典赋值
        for (SysDictAllItem item : list) {
            JSONObject entity = new JSONObject().fluentPut("dictCode", item.getItemValue()).fluentPut("dictName", item.getItemText());
//            List<ShopInfoVo> enableShopList = shopInfoMapper.selectVoByPlatform(item.getItemValue());
//            entity.fluentPut("enableList", enableShopList);
//            List<ShopInfoVo> disenableShopList = shopInfoMapper.selectVoJoinListingByPlatform(item.getItemValue(),fnsku);
//            entity.fluentPut("disenableList", disenableShopList);
            result.add(entity);
        }
        return result;
    }


    /**
     * 是否走导出中心
     */
    @Override
    public boolean getBackgroundExportFlag(PurchasePlansSearchParamPo search) {
        return false;
    }

    /**
     * 批量删除指定主键列表关联的所有子表数据(此方法不包括事务，必须由其它已包括事务的方法调用)
     */
    protected void batchDeleteChilds(List<Long> ids) throws Exception {
        // 处理子表字段 采购计划详情(procurementPlan) 对应的对象
        PurchasePlansProductSearchParamPo procurementPlanSearchParam =
                new PurchasePlansProductSearchParamPo();
        procurementPlanSearchParam.setDisableDataPremissions(true);
        procurementPlanSearchParam.setParentPurchasePlansInList(ids);
        PurchasePlansProductMapper localPurchasePlansProductMapper = ContextHolder
                .getApplicationContext()
                .getBean(PurchasePlansProductMapper.class);
        procurementPlanSearchParam.applySqlSegments();
        List<Long> procurementPlanIdList =
                localPurchasePlansProductMapper.searchIds(procurementPlanSearchParam);
        if (!procurementPlanIdList.isEmpty()) {
            PurchasePlansProductService localPurchasePlansProductService =
                    ContextHolder
                            .getApplicationContext()
                            .getBean(PurchasePlansProductService.class);
            localPurchasePlansProductService.batchDelete(procurementPlanIdList);
        }
    }

    /**
     * 删除采购计划
     *
     * @param id 采购计划的主键
     * @return 删除是否成功
     */
    @Override
    public boolean delete(Long id) throws Exception {
        batchDeleteChilds(Collections.singletonList(id));
        return this.removeById(id);
    }

    /**
     * 批量删除采购计划
     *
     * @param ids 采购计划的主键列表
     * @return 删除是否成功
     */
    @Override
    public boolean batchDelete(List<Long> ids) throws Exception {
        batchDeleteChilds(ids);
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 导入单行处理
     *
     * @param row            要导入的行
     * @param rowNumber      所在行号
     * @param vo             输出VO,可空
     * @param variables      变量池，用于在一次导入过程中保存临时信息
     * @param allowOverrides 允许覆盖
     * @return 导入行对象
     */
    protected ImportRow<PurchasePlansImportPo> excelToImportRow(
            PurchasePlansImportPo row,
            Integer rowNumber,
            PurchasePlansVo vo,
            Map<String, Object> variables,
            boolean allowOverrides
    ) {
        ImportRow<PurchasePlansImportPo> ir =
                new ImportRow<PurchasePlansImportPo>();
        ir.setRowNumber(rowNumber);
        ir.setRow(row);
        if (SheetUtils.isAllFieldsEmpty(row)) {
            ir.setEmptyRow(true);
            ir.addError("", "空行");
            return ir;
        }
        final Set<ConstraintViolation<PurchasePlansImportPo>> validate =
                validator.validate(row);
        boolean isOutputVo = vo != null;
        if (!isOutputVo) {
            vo = new PurchasePlansVo();
        }
        BeanUtils.copyProperties(row, vo);
        // 收集基本校验错误
        ir.initErrors(validate, PurchasePlansImportPo.class);

        // 一般数据处理
        if (StringUtils.hasText(row.getUrgent())) {
            List<SysDictAllItem> urgentItems =
                    (List<SysDictAllItem>) variables.computeIfAbsent(
                            "urgent",
                            n -> sysDictService.getDictItems("SCM00008")
                    );
            PurchasePlansVo finalVo = vo;
            urgentItems
                    .stream()
                    .filter(i -> i.getItemText().equals(row.getUrgent()))
                    .findFirst()
                    .ifPresent(i -> finalVo.setUrgent(i.getItemValue()));
        }
        if (StringUtils.hasText(row.getManPlan())) {
            List<SysDictAllItem> manPlanItems =
                    (List<SysDictAllItem>) variables.computeIfAbsent(
                            "manPlan",
                            n -> sysDictService.getDictItems("SCM00101")
                    );
            PurchasePlansVo finalVo = vo;
            manPlanItems
                    .stream()
                    .filter(i -> i.getItemText().equals(row.getManPlan()))
                    .findFirst()
                    .ifPresent(i -> finalVo.setManPlan(i.getItemValue()));
        }
        if (StringUtils.hasText(row.getProType())) {
            List<SysDictAllItem> proTypeItems =
                    (List<SysDictAllItem>) variables.computeIfAbsent(
                            "proType",
                            n -> sysDictService.getDictItems("SCM00016")
                    );
            PurchasePlansVo finalVo = vo;
            proTypeItems
                    .stream()
                    .filter(i -> i.getItemText().equals(row.getProType()))
                    .findFirst()
                    .ifPresent(i -> finalVo.setProType(i.getItemValue()));
        }
        if (StringUtils.hasText(row.getStatus())) {
            vo.setStatus(PurchasePlansStatusEnums.toValue(row.getStatus()));
            if (vo.getStatus() == null) {
                ir.addError(
                        "status",
                        "状态只能是：“待采购”, “已完成”, “已驳回”, “已作废”, “待审批”, “待提交”"
                );
            }
        }
        vo.setQuantityPurchased(SheetUtils.tryToLong(row.getQuantityPurchased()));
        // 处理关关联字段“全部国家”
        if (
                !SheetUtils.isBlank(row.getCountryName()) &&
                        SheetUtils.isBlank(row.getCountry())
        ) {
            SysAreaSearchParamPo countrySearchParam = new SysAreaSearchParamPo();
            countrySearchParam.setLimit(2); // 最大只返回二行记录
            countrySearchParam.setNameEq(SheetUtils.trim(row.getCountryName()));
            countrySearchParam.applySqlSegments();
            List<SysArea> countryMatches = sysAreaMapper.search(countrySearchParam);
            if (countryMatches.size() == 0) {
                ir.addError("countryName", "未能在地区中找到匹配的数据");
            } else if (countryMatches.size() > 1) {
                ir.addError("countryName", "关联不唯一，在地区中找到多个匹配的数据");
            } else {
                vo.setCountry(countryMatches.get(0).getId());
            }
        }
        // 处理关关联字段“全部店铺”
        if (
                !SheetUtils.isBlank(row.getShopShopName()) &&
                        SheetUtils.isBlank(row.getShop())
        ) {
            ShopInfoSearchParamPo shopSearchParam = new ShopInfoSearchParamPo();
            shopSearchParam.setLimit(2); // 最大只返回二行记录
            shopSearchParam.setShopNameEq(SheetUtils.trim(row.getShopShopName()));
            shopSearchParam.applySqlSegments();
            List<ShopInfoItemVo> shopMatches = shopInfoMapper.search(shopSearchParam);
            if (shopMatches.size() == 0) {
                ir.addError("shopShopName", "未能在店铺中找到匹配的数据");
            } else if (shopMatches.size() > 1) {
                ir.addError("shopShopName", "关联不唯一，在店铺中找到多个匹配的数据");
            } else {
                vo.setShop(shopMatches.get(0).getId());
            }
        }
        // 处理关关联字段“供应商”
        if (
                !SheetUtils.isBlank(row.getSupplySupplierName()) &&
                        SheetUtils.isBlank(row.getSupply())
        ) {
            SupplierSearchParamPo supplySearchParam = new SupplierSearchParamPo();
            supplySearchParam.setLimit(2); // 最大只返回二行记录
            supplySearchParam.setSupplierNameEq(
                    SheetUtils.trim(row.getSupplySupplierName())
            );
            supplySearchParam.applySqlSegments();
            List<SupplierItemVo> supplyMatches = supplierMapper.search(
                    supplySearchParam
            );
            if (supplyMatches.size() == 0) {
                ir.addError("supplySupplierName", "未能在供应商中找到匹配的数据");
            } else if (supplyMatches.size() > 1) {
                ir.addError(
                        "supplySupplierName",
                        "关联不唯一，在供应商中找到多个匹配的数据"
                );
            } else {
                vo.setSupply(supplyMatches.get(0).getId());
            }
        }
        // 处理关关联字段“采购仓库”
        if (
                !SheetUtils.isBlank(row.getPurchaseWarePurchaseOrderCode()) &&
                        SheetUtils.isBlank(row.getPurchaseWare())
        ) {
            PurchaseOrderSearchParamPo purchaseWareSearchParam =
                    new PurchaseOrderSearchParamPo();
            purchaseWareSearchParam.setLimit(2); // 最大只返回二行记录
            purchaseWareSearchParam.setPurchaseOrderCodeEq(
                    SheetUtils.trim(row.getPurchaseWarePurchaseOrderCode())
            );
            purchaseWareSearchParam.applySqlSegments();
            List<PurchaseOrderItemVo> purchaseWareMatches =
                    purchaseOrderMapper.search(purchaseWareSearchParam);
            if (purchaseWareMatches.size() == 0) {
                ir.addError(
                        "purchaseWarePurchaseOrderCode",
                        "未能在采购订单中找到匹配的数据"
                );
            } else if (purchaseWareMatches.size() > 1) {
                ir.addError(
                        "purchaseWarePurchaseOrderCode",
                        "关联不唯一，在采购订单中找到多个匹配的数据"
                );
            } else {
                vo.setPurchaseWare(purchaseWareMatches.get(0).getId());
            }
        }
        // 处理关关联字段“创建人”
        if (
                !SheetUtils.isBlank(row.getCreateNameName()) &&
                        SheetUtils.isBlank(row.getCreateName())
        ) {
            SysUserSearch createNameSearchParam = new SysUserSearch();
            createNameSearchParam.setLimit(2); // 最大只返回二行记录
            createNameSearchParam.setNameEq(SheetUtils.trim(row.getCreateNameName()));
            createNameSearchParam.applySqlSegments();
            List<SysUserItemVo> createNameMatches = sysUserMapper.search(
                    createNameSearchParam
            );
            if (createNameMatches.size() == 0) {
                ir.addError("createNameName", "未能在用户中找到匹配的数据");
            } else if (createNameMatches.size() > 1) {
                ir.addError("createNameName", "关联不唯一，在用户中找到多个匹配的数据");
            } else {
                vo.setCreateName(createNameMatches.get(0).getId());
            }
        }

        return ir;
    }

    /**
     * 导入预览
     *
     * @param sysFile        已上传到系统的文件
     * @param templateId     要使用的模版编号
     * @param allowOverrides 允许覆盖
     */
    @Override
    public List<ImportRow<PurchasePlansImportPo>> importPreview(
            SysFile sysFile,
            Long templateId,
            boolean allowOverrides
    ) throws Exception {
        SysImportTemplate importTemplate = null;
        if (templateId != null && templateId > 0) {
            importTemplate = importTemplateService.getById(templateId);
            if (importTemplate == null) {
                throw new RException("指定的模版不可用！");
            }
        }
        Map<String, Object> variables = new HashMap<>();
        try (InputStream in = fileService.read(sysFile)) {
            List<ImportRow<PurchasePlansImportPo>> rows = new ArrayList<>();

            // 读取 Excel 到 rows
            EasyExcel
                    .read(in)
                    .useDefaultListener(false)
                    .registerReadListener(
                            new TemplateModelBuildEventListener<>(
                                    PurchasePlansImportPo.class,
                                    importTemplate
                            ) {
                                @Override
                                public void invokeRow(
                                        PurchasePlansImportPo row,
                                        AnalysisContext cxt
                                ) {
                                    PurchasePlansVo vo = new PurchasePlansVo();
                                    ImportRow<PurchasePlansImportPo> ir = excelToImportRow(
                                            row,
                                            cxt.readRowHolder().getRowIndex(),
                                            vo,
                                            variables,
                                            allowOverrides
                                    );
                                    if (ir.isEmptyRow()) {
                                        return;
                                    }
                                    if (allowOverrides) {
                                        // FIXME: 未在平台上配置此对象《作为导入唯一识别》的《逻辑分组》配置，无法处理更新模式
                                    }
                                    rows.add(ir);
                                }
                            }
                    )
                    .sheet()
                    .doRead();

            return rows;
        }
    }

    /**
     * 完成导入
     *
     * @param rows 预导入的行信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importDone(
            List<ImportRow<PurchasePlansImportPo>> rows,
            Long templateId
    ) throws Exception {
        SysImportTemplate importTemplate = null;
        if (templateId != null && templateId > 0) {
            importTemplate = importTemplateService.getById(templateId);
            if (importTemplate == null) {
                throw new RException("指定的模版不可用！");
            }
        }
        String sysImportBatchNo = StrUtil.concat(
                true,
                DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_MS_PATTERN),
                IdUtil.nanoId(4)
        );
        List<Long> importedIdList = new ArrayList<>();
        Map<String, Object> variables = new HashMap<>();
        for (int i = 0; i < rows.size(); i++) {
            ImportRow<PurchasePlansImportPo> row = rows.get(i);
            Long updateId = row.getUpdateId();
            PurchasePlansVo vo = new PurchasePlansVo();
            row =
                    excelToImportRow(row.getRow(), row.getRowNumber(), vo, variables, true);
            if (row.getErrors() != null && row.getErrors().size() > 0) {
                throw new RException(
                        String.format(
                                "第%d行（Excel第%d行）数据验证错误：%s",
                                i + 1,
                                row.getRowNumber(),
                                row.getJoinErrorMessage()
                        )
                );
            }
            if (updateId != null) {
                vo.setId(updateId);
            }
            // 重建"创建批次号"
            if (
                    importTemplate != null &&
                            importTemplate
                                    .getSetting()
                                    .getMappings()
                                    .stream()
                                    .anyMatch(m ->
                                            Boolean.TRUE.equals(m.getNeedRecreate()) &&
                                                    "ppgSn".equals(m.getField())
                                    )
            ) {
                vo.setPpgSn(
                        sysSerialService.getNewSerial(
                                new NewSerialRequest(null, "PurchasePlans.ppgSn")
                        )
                );
            }

            SheetUtils.setSysImportTemplateName(vo, importTemplate.getTitle());
            SheetUtils.setSysImportBatchNo(vo, sysImportBatchNo);
            if (updateId == null) {
                importedIdList.add(create(vo));
            } else {
                update(vo);
                importedIdList.add(vo.getId());
            }
        }
    }

    /**
     * 导出
     *
     * @param templateId 使用模版（可以是 null 或 0 表示系统默认导出模版）
     * @param search     查询条件
     */
    @Override
    public void export(
            Long templateId,
            PurchasePlansSearchParamPo search,
            HttpServletResponse response
    ) throws Exception {
        if (templateId != null && templateId > 0) {
            SysImportTemplate sysImportTemplate = importTemplateService.getById(
                    templateId
            );
            if (sysImportTemplate == null) {
                throw new RException("所选的导出模版不可用");
            }
            CustomExcelExport.CustomExcelExportOptions exportOptions =
                    new CustomExcelExport.CustomExcelExportOptions();
            exportOptions.setImportTemplate(sysImportTemplate);
            exportOptions.setSearchParam(search);
            exportOptions.setMainService(this);
            exportOptions.addFieldMappingOption(
                    "status",
                    new CustomExcelExport.FieldMappingOption()
                            .setConverter((o, r) -> PurchasePlansStatusEnums.toLabel((String) o))
            );
            exportOptions.addFieldMappingOption(
                    "createName",
                    new CustomExcelExport.FieldMappingOption()
                            .setConverter((o, r) ->
                                    String.valueOf(((PurchasePlansItemVo) r).getCreateNameName())
                            )
            );
            exportOptions.addStringFields(
                    "ppgSn",
                    "quantityPlan",
                    "remark",
                    "status",
                    "createNameName",
                    "createName2"
            );
            customExcelExport.run(exportOptions, response.getOutputStream());
            return;
        }
        search.setPageSize(EXPORT_QUERY_MAX_SIZE);
        int current = 1;
        List<PurchasePlansExcelPo> items = new ArrayList<PurchasePlansExcelPo>();
        while (true) {
            search.setCurrent(current);
            Page<PurchasePlansItemVo> page = this.pageSearch(search);
            if (page.getTotal() > EXPORT_MAX_SIZE) {
                throw new RException("导出记录数超出限制！");
            }
            List<PurchasePlansItemVo> list = page.getRecords();
            if (list.isEmpty()) {
                break;
            }
            for (PurchasePlansItemVo item : list) {
                PurchasePlansExcelPo excel = new PurchasePlansExcelPo();
                BeanUtils.copyProperties(item, excel);
                excel.setStatus(PurchasePlansStatusEnums.toLabel(item.getStatus()));

                items.add(excel);
            }
            if (list.size() < EXPORT_QUERY_MAX_SIZE) {
                break;
            }
            current++;
        }

        String fileName = String.format(
                "采购计划(%s).xlsx",
                DateUtils.toString(null, DateUtils.PATTERN_DATE_TIME)
        );
        ResponseUtils.setAttachmentFileName(response, fileName);
        EasyExcel
                .write(response.getOutputStream())
                .registerWriteHandler(new CustomExcelHandler())
                .sheet()
                .head(PurchasePlansExcelPo.class)
                .doWrite(items);
    }

    /**
     * 通过创建批次号查询采购计划主键(重复时返回最新的主键)
     */
    @Override
    public Long getIdByPpgSn(String ppgSn) {
        return this.baseMapper.getIdByPpgSn(ppgSn);
    }

    /**
     * 通过创建批次号查询采购计划多主键(重复时返回最新的主键)
     */
    @Override
    public List<Long> getIdsByPpgSn(List<String> list) {
        return this.baseMapper.getIdsByPpgSn(list);
    }

    /**
     * 通过采购计划主键查询创建批次号
     */
    @Override
    public String getPpgSnById(Long id) {
        return this.baseMapper.getPpgSnById(id);
    }

    /**
     * 通过采购计划主键查询创建批次号列表
     */
    @Override
    public List<String> getPpgSnByIds(List<Long> ids) {
        List<Map<String, Object>> maps = this.baseMapper.getPpgSnByIds(ids);
        // 将返回重新排列为输入相同顺序
        return ids
                .stream()
                .map(id -> {
                    Optional<Map<String, Object>> optionalMap = maps
                            .stream()
                            .filter(map -> id.equals(map.get("id")))
                            .findFirst();
                    return optionalMap
                            .map(stringObjectMap -> (String) stringObjectMap.get("ppgSn"))
                            .orElse(null);
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取详情
     *
     * @param id 采购计划的主键
     */
    @Override
    public PurchasePlansVo getVoById(Long id) throws Exception {
        PurchasePlansVo vo = this.baseMapper.selectVoById(id);
        if (vo == null) {
            return null;
        }

        PurchasePlansProductSearchParamPo procurementPlanSearchParamPo =
                new PurchasePlansProductSearchParamPo();
        procurementPlanSearchParamPo.setParentPurchasePlans(id);
        procurementPlanSearchParamPo.applySqlSegments();
        vo.setProcurementPlan(
                purchasePlansProductService.search(procurementPlanSearchParamPo)
        );

        return vo;
    }

    /**
     * 分页查询采购计划
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<PurchasePlansItemVo> pageSearch(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> page = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return page;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return page;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        page = this.baseMapper.pageSearch(search.toPage(), search);
        List<Long> recordIds = page
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return page;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return page;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        page
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });
        return page;
    }

    /**
     * 通用方法，为列表添加子表数据
     *
     * @param pageResult 列表结果
     */
    protected Page<?> appendWithChild(Page<PurchasePlansItemVo> pageResult) {
        Page<JSONObject> withChildPage = new Page<>(
                pageResult.getCurrent(),
                pageResult.getSize(),
                pageResult.getTotal()
        );
        withChildPage.setRecords(new ArrayList<>());
        for (PurchasePlansItemVo itemVo : pageResult.getRecords()) {
            JSONObject itemJson = (JSONObject) JSONObject.toJSON(itemVo);
            withChildPage.getRecords().add(itemJson);

            PurchasePlansProductSearchParamPo procurementPlanSearchParamPo =
                    new PurchasePlansProductSearchParamPo();
            procurementPlanSearchParamPo.setPageSize(5);
            procurementPlanSearchParamPo.setParentPurchasePlans(itemVo.getId());
            itemJson.put(
                    "procurementPlan",
                    purchasePlansProductService.pageSearch(procurementPlanSearchParamPo)
            );
        }
        return withChildPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> pageWithChildSearch(PurchasePlansSearchParamPo search) {
        return appendWithChild(pageSearch(search));
    }

    /**
     * 仅获取子表的查询结果
     *
     * @param parentId 主表编号
     * @param search   查询条件(仅使用分页参数，不处理其它条件)
     * @return
     */
    @Override
    public Page<?> pageOnlyChildSearch(
            Long parentId,
            PurchasePlansSearchParamPo search
    ) {
        PurchasePlansSearchParamPo parentSearchParam =
                new PurchasePlansSearchParamPo();
        parentSearchParam.setIdEq(parentId);
        List<PurchasePlansItemVo> parentList = search(parentSearchParam);
        if (parentList.size() > 0) {
            PurchasePlansItemVo itemVo = parentList.get(0);

            PurchasePlansProductSearchParamPo procurementPlanSearchParamPo =
                    new PurchasePlansProductSearchParamPo();
            procurementPlanSearchParamPo.setPageSize(search.getPageSize());
            procurementPlanSearchParamPo.setCurrent(search.getCurrent());
            procurementPlanSearchParamPo.setParentPurchasePlans(itemVo.getId());
            return purchasePlansProductService.pageSearch(
                    procurementPlanSearchParamPo
            );
        } else {
            return null;
        }
    }

    /**
     * 采购计划快速查询选项(有缓存)
     *
     * @param search 查询条件
     * @return 选项结果
     */
    @Override
    @Cacheable(
            value = CacheConfig.FAST,
            key = "'PurchasePlansOptions::' + @userInfo.userTypeAndId + '::' + #search.keyword"
    )
    public List<OptionItem<PurchasePlansItemVo>> searchOptions(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.searchOptions(search);
    }

    /**
     * 列表查询采购计划
     *
     * @param search 查询条件
     * @return 采购计划列表查询结果
     */
    @Override
    public List<PurchasePlansItemVo> search(PurchasePlansSearchParamPo search) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.search(search);
    }

    /**
     * 查询采购计划最后更新时间
     *
     * @param search 查询条件
     * @return 采购计划最后更新时间
     */
    @Override
    public Date searchLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.searchLastUpdateTime(search);
    }

    /**
     * 作废
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancellation(List<Long> ids) throws Exception {
        executeScriptEngine.callJsInResource(
                "scripts/PurchasePlans_cancellation.js",
                ids,
                null,
                this
        );
    }

    /**
     * 采购计划看板
     */
    @Override
    public void purchasingPlanBoard(List<Long> ids) throws Exception {
        if (purchasePlansPurchasingPlanBoardAction == null) {
            throw new Exception("此操作当前不可用");
        }
        purchasePlansPurchasingPlanBoardAction.run(ids);
    }

    /**
     * 拉取采购领星计划
     */
    @Override
    public void syncPurchasePlans() throws Exception {
        if (purchasePlansSyncPurchasePlansAction == null) {
            throw new Exception("此操作当前不可用");
        }
        purchasePlansSyncPurchasePlansAction.run();
    }

    /**
     * 视图选项卡标题上显示计数
     */

    @Override
    public Map getTabHeaderShowTotal() {
        PurchasePlansSearchParamPo search = new PurchasePlansSearchParamPo();
        return MapUtil
                .builder()
                .put("Complete", this.baseMapper.completeCount(search))
                .put("WaitingSubm", this.baseMapper.waitingSubmCount(search))
                .put(
                        "Theapprovalispending",
                        this.baseMapper.theapprovalispendingCount(search)
                )
                .put("Tobepurchased", this.baseMapper.tobepurchasedCount(search))
                .put("Completed", this.baseMapper.completedCount(search))
                .put("Rejected", this.baseMapper.rejectedCount(search))
                .put("Havebeenvoided", this.baseMapper.havebeenvoidedCount(search))
                .build();
    }

    /**
     * 全部
     */
    @Override
    public List<PurchasePlansItemVo> complete(PurchasePlansSearchParamPo search) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.complete(search);
    }

    /**
     * 全部分页
     */
    @Override
    public Page<PurchasePlansItemVo> completePage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> completePage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return completePage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return completePage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        completePage = this.baseMapper.completePage(search.toPage(), search);
        List<Long> recordIds = completePage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return completePage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return completePage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        completePage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return completePage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> completePageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(completePage(search));
    }

    /**
     * 查询全部最后更新时间
     *
     * @param search 查询条件
     * @return 全部最后更新时间
     */
    @Override
    public Date completeLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.completeLastUpdateTime(search);
    }

    /**
     * 待提交
     */
    @Override
    public List<PurchasePlansItemVo> waitingSubm(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.waitingSubm(search);
    }

    /**
     * 待提交分页
     */
    @Override
    public Page<PurchasePlansItemVo> waitingSubmPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> waitingSubmPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return waitingSubmPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return waitingSubmPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        waitingSubmPage = this.baseMapper.waitingSubmPage(search.toPage(), search);
        List<Long> recordIds = waitingSubmPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return waitingSubmPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return waitingSubmPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        waitingSubmPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return waitingSubmPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> waitingSubmPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(waitingSubmPage(search));
    }

    /**
     * 查询待提交最后更新时间
     *
     * @param search 查询条件
     * @return 待提交最后更新时间
     */
    @Override
    public Date waitingSubmLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.waitingSubmLastUpdateTime(search);
    }

    /**
     * 待审批
     */
    @Override
    public List<PurchasePlansItemVo> theapprovalispending(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.theapprovalispending(search);
    }

    /**
     * 待审批分页
     */
    @Override
    public Page<PurchasePlansItemVo> theapprovalispendingPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> theapprovalispendingPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return theapprovalispendingPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return theapprovalispendingPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        theapprovalispendingPage =
                this.baseMapper.theapprovalispendingPage(search.toPage(), search);
        List<Long> recordIds = theapprovalispendingPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return theapprovalispendingPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return theapprovalispendingPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        theapprovalispendingPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return theapprovalispendingPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> theapprovalispendingPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(theapprovalispendingPage(search));
    }

    /**
     * 查询待审批最后更新时间
     *
     * @param search 查询条件
     * @return 待审批最后更新时间
     */
    @Override
    public Date theapprovalispendingLastUpdateTime(
            PurchasePlansSearchParamPo search
    ) {
        return this.baseMapper.theapprovalispendingLastUpdateTime(search);
    }

    /**
     * 待采购
     */
    @Override
    public List<PurchasePlansItemVo> tobepurchased(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.tobepurchased(search);
    }

    /**
     * 待采购分页
     */
    @Override
    public Page<PurchasePlansItemVo> tobepurchasedPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> tobepurchasedPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return tobepurchasedPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return tobepurchasedPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        tobepurchasedPage =
                this.baseMapper.tobepurchasedPage(search.toPage(), search);
        List<Long> recordIds = tobepurchasedPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return tobepurchasedPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return tobepurchasedPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        tobepurchasedPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return tobepurchasedPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> tobepurchasedPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(tobepurchasedPage(search));
    }

    /**
     * 查询待采购最后更新时间
     *
     * @param search 查询条件
     * @return 待采购最后更新时间
     */
    @Override
    public Date tobepurchasedLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.tobepurchasedLastUpdateTime(search);
    }

    /**
     * 已完成
     */
    @Override
    public List<PurchasePlansItemVo> completed(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.completed(search);
    }

    /**
     * 已完成分页
     */
    @Override
    public Page<PurchasePlansItemVo> completedPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> completedPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return completedPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return completedPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        completedPage = this.baseMapper.completedPage(search.toPage(), search);
        List<Long> recordIds = completedPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return completedPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return completedPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        completedPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return completedPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> completedPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(completedPage(search));
    }

    /**
     * 查询已完成最后更新时间
     *
     * @param search 查询条件
     * @return 已完成最后更新时间
     */
    @Override
    public Date completedLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.completedLastUpdateTime(search);
    }

    /**
     * 已驳回
     */
    @Override
    public List<PurchasePlansItemVo> rejected(PurchasePlansSearchParamPo search) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.rejected(search);
    }

    /**
     * 已驳回分页
     */
    @Override
    public Page<PurchasePlansItemVo> rejectedPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> rejectedPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return rejectedPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return rejectedPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        rejectedPage = this.baseMapper.rejectedPage(search.toPage(), search);
        List<Long> recordIds = rejectedPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return rejectedPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return rejectedPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        rejectedPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return rejectedPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> rejectedPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(rejectedPage(search));
    }

    /**
     * 查询已驳回最后更新时间
     *
     * @param search 查询条件
     * @return 已驳回最后更新时间
     */
    @Override
    public Date rejectedLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.rejectedLastUpdateTime(search);
    }

    /**
     * 已作废
     */
    @Override
    public List<PurchasePlansItemVo> havebeenvoided(
            PurchasePlansSearchParamPo search
    ) {
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        return this.baseMapper.havebeenvoided(search);
    }

    /**
     * 已作废分页
     */
    @Override
    public Page<PurchasePlansItemVo> havebeenvoidedPage(
            PurchasePlansSearchParamPo search
    ) {
        Page<PurchasePlansItemVo> havebeenvoidedPage = new Page<>();
        if (
                Objects.nonNull(search.getWfStatus()) &&
                        StringUtils.hasText(search.getFormName())
        ) {
            List<WorkFlow> workFlowList = workFlowService.queryWfListByStatus(
                    search.getWfStatus(),
                    search.getFormName()
            );
            if (CollectionUtils.isEmpty(workFlowList)) {
                return havebeenvoidedPage;
            }
            List<Long> ids = workFlowList
                    .stream()
                    .map(WorkFlow::getRecordId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(ids)) {
                return havebeenvoidedPage;
            }
            search.setSelectedIds(ids);
        }
        search.initSort("t.id desc", "t.", SORT_FIELD_MAP);
        havebeenvoidedPage =
                this.baseMapper.havebeenvoidedPage(search.toPage(), search);
        List<Long> recordIds = havebeenvoidedPage
                .getRecords()
                .stream()
                .map(PurchasePlansItemVo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIds)) {
            return havebeenvoidedPage;
        }
        List<WorkFlow> workFlows = workFlowService.queryWfList(
                recordIds,
                Collections.singletonList(search.getFormName())
        );
        if (CollectionUtils.isEmpty(workFlows)) {
            return havebeenvoidedPage;
        }
        Map<Long, Integer> flowMap = workFlows
                .stream()
                .collect(Collectors.toMap(WorkFlow::getRecordId, WorkFlow::getWfStatus));
        havebeenvoidedPage
                .getRecords()
                .stream()
                .forEach(item -> {
                    if (!flowMap.containsKey(item.getId())) {
                        return;
                    }
                    item.setWfStatus(flowMap.get(item.getId()));
                });

        return havebeenvoidedPage;
    }

    /**
     * 获取包括子表的查询结果
     *
     * @param search 查询条件
     * @return 采购计划分页查询结果
     */
    @Override
    public Page<?> havebeenvoidedPageWithChildSearch(
            PurchasePlansSearchParamPo search
    ) {
        return appendWithChild(havebeenvoidedPage(search));
    }

    /**
     * 查询已作废最后更新时间
     *
     * @param search 查询条件
     * @return 已作废最后更新时间
     */
    @Override
    public Date havebeenvoidedLastUpdateTime(PurchasePlansSearchParamPo search) {
        return this.baseMapper.havebeenvoidedLastUpdateTime(search);
    }

    /**
     * 获取采购计划生成
     */
    @Override
    public void detailedInfo(PurchasePlansDetailedInfoParam param) {
        this.baseMapper.detailedInfo(param);
    }
}
